package remotecon

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/kevinburke/ssh_config"

	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)

// const conTimeout = 3000

// SSHConfig ssh 连接配置
type SSHConfig struct {
	Usr      string
	Host     string
	Port     string
	PassWord string
}

func (pr *SSHConfig) String() string {
	ostr := fmt.Sprintf("SSHConfig->[Host:%s, usr:%s, Port:%s, PassWord:%s]\n", pr.Host, pr.Usr, pr.Port, pr.PassWord)
	return ostr
}

// RemoteCon 远程控制帮助类
type RemoteCon struct {
	auth         []ssh.AuthMethod
	addr         string
	clientConfig *ssh.ClientConfig
	sshClient    *ssh.Client
	sshSession   *ssh.Session
	sftpClient   *sftp.Client
	er           error
	isconnected  bool
}

func (pr *RemoteCon) getSSHConfig(sshkey string) *SSHConfig {
	Usr := ssh_config.Get(sshkey, "User")
	Port := ssh_config.Get(sshkey, "Port")
	Host := ssh_config.Get(sshkey, "HostName")
	return &SSHConfig{
		Usr:  Usr,
		Host: Host,
		Port: Port,
	}
}

func (pr *RemoteCon) doSftp(sc *SSHConfig) bool {
	issuc := pr.doSSH(sc)
	if !issuc {
		return false
	}
	pr.sftpClient, pr.er = sftp.NewClient(pr.sshClient)
	if pr.er != nil {
		fmt.Println(pr.er)
		return false
	}
	return true
}

func (pr *RemoteCon) doSSH(sc *SSHConfig) bool {
	fmt.Println(sc.String())
	// get auth method
	pr.auth = append(pr.auth, ssh.Password(sc.PassWord))
	pr.clientConfig = &ssh.ClientConfig{
		User:            sc.Usr,
		Auth:            pr.auth,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	// connet to ssh
	addr := fmt.Sprintf("%s:%s", sc.Host, sc.Port)
	pr.sshClient, pr.er = ssh.Dial("tcp", addr, pr.clientConfig)
	if pr.er != nil {
		fmt.Println(pr.er)
		return false
	}
	return true
}

// Connect 连接远端
func (pr *RemoteCon) Connect(sshkey string, pwd string) bool {
	fmt.Printf("Connect to  .ssh config -><%s>", sshkey)

	if !pr.isconnected {
		pr.auth = make([]ssh.AuthMethod, 0)
		sshconfig := pr.getSSHConfig(sshkey)
		sshconfig.PassWord = pwd
		pr.isconnected = pr.doSftp(sshconfig)
	}
	if pr.isconnected {
		fmt.Println("Connect suc!")
		return true
	}
	return false
}

// ConnectByStruct 连接远端
func (pr *RemoteCon) ConnectByStruct(sc *SSHConfig) bool {
	fmt.Printf("Connect to  .ssh config -><%s>", sc.String())

	if !pr.isconnected {
		pr.isconnected = pr.doSftp(sc)
	}
	if pr.isconnected {
		fmt.Println("Connect suc!")
		return true
	}
	return false
}

// UploadFile 上传文件到远端路径
func (pr *RemoteCon) UploadFile(lfpath string, rfpath string) {
	srcFile, err := os.Open(lfpath)
	if err != nil {
		fmt.Println("os.Open error : ", lfpath)
		log.Fatal(err)

	}
	defer srcFile.Close()

	var remoteFileName = path.Base(lfpath)

	dstFile, err := pr.sftpClient.Create(path.Join(rfpath, remoteFileName))
	if err != nil {
		fmt.Println("sftpClient.Create error : ", path.Join(rfpath, remoteFileName))
		log.Fatal(err)

	}
	defer dstFile.Close()

	ff, err := ioutil.ReadAll(srcFile)
	if err != nil {
		fmt.Println("ReadAll error : ", lfpath)
		log.Fatal(err)

	}
	dstFile.Write(ff)
	fmt.Println(lfpath + "  copy file to remote server finished!")
}

// UploadDir 上传文件夹到远端路径
func (pr *RemoteCon) UploadDir(lfdir string, rpath string) {
	localFiles, err := ioutil.ReadDir(lfdir)
	if err != nil {
		log.Fatal("read dir list fail ", err)
	}

	// try to create dir
	pr.sftpClient.Mkdir(rpath)

	for _, backupDir := range localFiles {
		localFilePath := path.Join(lfdir, backupDir.Name())
		remoteFilePath := path.Join(rpath, backupDir.Name())
		if backupDir.IsDir() {
			err := pr.sftpClient.MkdirAll(remoteFilePath)
			if err != nil {
				pr.er = err
				log.Fatalf("create remote dir failed!err:%s", pr.er)
			}
			pr.UploadDir(localFilePath, remoteFilePath)
		} else {
			pr.UploadFile(path.Join(lfdir, backupDir.Name()), rpath)
		}
	}
	fmt.Println(lfdir + "  copy directory to remote server finished!")
}

// RunCmd 远端执行命令
func (pr *RemoteCon) RunCmd(cmd string, params ...string) bool {
	if pr.sshSession == nil {
		pr.sshSession, pr.er = pr.sshClient.NewSession()
		if pr.er != nil { //创建客户端
			fmt.Println(pr.er)
			return false
		}
		pr.sshSession.Stdout = os.Stdout
		pr.sshSession.Stderr = os.Stderr
		pr.sshSession.Stdin = os.Stdin
	}
	fmt.Println("=======stsrt run cmd=======")
	pr.sshSession.Run(cmd)
	fmt.Println("============end============")
	return true
}
